package cn.changeforyou.web.dao.mybatis.ext.metaInfo.parser;

import cn.changeforyou.base.exception.ExceptionFactory;
import cn.changeforyou.web.dao.mybatis.ext.dao.BaseDao;
import cn.changeforyou.web.dao.mybatis.ext.exception.DaoExceptionEnum;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder.DeleteBuilder;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder.InsertBuilder;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder.SelectBuilder;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder.UpdateBuilder;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.*;
import cn.changeforyou.web.dao.mybatis.ext.utils.MybatisUtil;
import cn.changeforyou.web.utils.ReflectUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.reflectiveObjects.TypeVariableImpl;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * MappedStatementMetaInfo的持有器, 负责新建, 获取和MappedStatementMetaInfo
 *
 * @author zhyu
 * @version 1.0
 * @date 2022/2/10 9:48
 */
public class MappedStatementMetaInfoHolder {
    /**
     * MappedStatementMetaInfo的缓存 key是
     */
    static Map<String, MappedStatementMetaInfo> mappedStatementMetaInfoCache = new ConcurrentHashMap<>(132);

    /**
     * 框架的MappedStatementId
     */
    static Set<String> oursMappedStatementId = new ConcurrentHashSet<>(128);
    /**
     * 其他的MappedStatementId
     */
    static Set<String> othersMappedStatementId = new ConcurrentHashSet<>(128);

    /**
     * 查看该MappedStatement是否是框架自己的
     *
     * @param mappedStatementId
     * @return
     */
    public static boolean isOursMappedStatementId(String mappedStatementId) {
        if (oursMappedStatementId.contains(mappedStatementId)) {
            return true;
        } else if (othersMappedStatementId.contains(mappedStatementId)) {
            return false;
        }
        String[] mappedStatementIdMetaInfo = getMappedStatementIdMetaInfo(mappedStatementId);
        try {
            Class<?> daoClass = Class.forName(mappedStatementIdMetaInfo[0]);
            Set<String> methodNames = ReflectUtil.getMethodNames(daoClass);
            Class<?> aClass = daoClass;
            if (BaseDao.class.isAssignableFrom(aClass) && methodNames.contains(mappedStatementIdMetaInfo[1])) {
                oursMappedStatementId.add(mappedStatementId);
                return true;
            } else {
                othersMappedStatementId.add(mappedStatementId);
                return false;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
        }
    }

    /**
     * Provider入口
     * 新建或者获取MappedStatementMetaInfo
     *
     * @param providerContext
     * @param param
     * @return
     */
    public static MappedStatementMetaInfo getOrCreateMappedStatementMetaInfoInProvider(ProviderContext providerContext, Object param) {
        Method method = providerContext.getMapperMethod();
        Class<?> mapperClass = providerContext.getMapperType();
        String mappedStatementId = getMappedStatementIdInProvider(mapperClass, method);
        String cacheKey = getCacheKeyByMappedStatementIdAndRuntimeParam(mappedStatementId, param);
        MappedStatementMetaInfo mappedStatementMetaInfo = getMappedStatementMetaInfoByCacheKey(cacheKey);
        if (null == mappedStatementMetaInfo) {
            MappedStatementBaseInfo mappedStatementBaseInfo = MappedStatementBaseInfoParser.parseInProvider(providerContext, param);
            mappedStatementMetaInfo = createMappedStatementMetaInfoByMappedStatementBaseInfo(mappedStatementBaseInfo);
            saveMappedStatementMetaInfoWithCacheKey(cacheKey, mappedStatementMetaInfo);
        }
        return mappedStatementMetaInfo;
    }


    /**
     * Interceptor入口
     * 根据mappedStatementId和动态传参获取MappedStatementMetaInfo, 其中动态传参要在方法名以Condition结尾时生效
     *
     * @return
     */
    public static MappedStatementMetaInfo getOrCreateMappedStatementMetaInfoInInterceptor(MappedStatement mappedStatement, Object runtimeParam) {
        String mappedStatementId = mappedStatement.getId();
        String cacheKey = getCacheKeyByMappedStatementIdAndRuntimeParam(mappedStatementId, runtimeParam);
        MappedStatementMetaInfo mappedStatementMetaInfo = getMappedStatementMetaInfoByCacheKey(cacheKey);
        if (null == mappedStatementMetaInfo) {
            MappedStatementBaseInfo mappedStatementBaseInfo = MappedStatementBaseInfoParser.parseInInterceptor(mappedStatement, runtimeParam);
            mappedStatementMetaInfo = createMappedStatementMetaInfoByMappedStatementBaseInfo(mappedStatementBaseInfo);
            saveMappedStatementMetaInfoWithCacheKey(cacheKey, mappedStatementMetaInfo);
        }
        return mappedStatementMetaInfo;
    }

    /**
     * 根据MappedStatementBaseInfo创建MappedStatementMetaInfo
     *
     * @param mappedStatementBaseInfo
     * @return
     */
    public static MappedStatementMetaInfo createMappedStatementMetaInfoByMappedStatementBaseInfo(MappedStatementBaseInfo mappedStatementBaseInfo) {
        MappedStatementMetaInfo mappedStatementMetaInfo = new MappedStatementMetaInfo();

        mappedStatementMetaInfo.setMappedStatementBaseInfo(mappedStatementBaseInfo);
        Class<?> entityClass = mappedStatementBaseInfo.getEntityClass();
        Class voClass = mappedStatementBaseInfo.getVoClass();
        Class dtoClass = mappedStatementBaseInfo.getDtoClass();
        Class conditionClass = mappedStatementBaseInfo.getConditionClass();

        EntityMetaInfo entityMetaInfo = EntityMetaInfoParser.parseEntity(entityClass);
        mappedStatementMetaInfo.setEntityMetaInfo(entityMetaInfo);

        if (null != voClass) {
            EntityMetaInfo voMetaInfo = EntityMetaInfoParser.parseVO(voClass);
            mappedStatementMetaInfo.setVoMetaInfo(voMetaInfo);
        }

        if (null != dtoClass) {
            EntityMetaInfo voMetaInfo = EntityMetaInfoParser.parseEntity(dtoClass);
            mappedStatementMetaInfo.setDtoMetaInfo(voMetaInfo);
        }

        if (null != conditionClass) {
            ConditionMetaInfo conditionMetaInfo = ConditionMetaInfoParser.parse(conditionClass);
            mappedStatementMetaInfo.setConditionMetaInfo(conditionMetaInfo);
        }

        SqlCommandType sqlCommandType = mappedStatementBaseInfo.getSqlCommandType();
        mappedStatementMetaInfo.setSqlCommandType(sqlCommandType);
        buildSqlAndResultMap(mappedStatementMetaInfo);
        return mappedStatementMetaInfo;
    }

    /**
     * 使用cacheKey保存MappedStatementMetaInfo
     *
     * @param cacheKey
     * @param mappedStatementMetaInfo
     */
    private static void saveMappedStatementMetaInfoWithCacheKey(String cacheKey, MappedStatementMetaInfo mappedStatementMetaInfo) {
        mappedStatementMetaInfoCache.put(cacheKey, mappedStatementMetaInfo);
    }

    /**
     * 根据缓存key获取MappedStatementMetaInfo
     *
     * @param key
     * @return
     */
    private static MappedStatementMetaInfo getMappedStatementMetaInfoByCacheKey(String key) {
        return mappedStatementMetaInfoCache.get(key);
    }

    /**
     * 在provider中获取MappedStatementId
     *
     * @param mapperClass
     * @param method
     * @return
     */
    private static String getMappedStatementIdInProvider(Class<?> mapperClass, Method method) {
        return mapperClass.getCanonicalName() + "." + method.getName();
    }

    /**
     * 构建sql和ResultMap
     *
     * @param mappedStatementMetaInfo
     */
    private static void buildSqlAndResultMap(MappedStatementMetaInfo mappedStatementMetaInfo) {
        SqlCommandType sqlCommandType = mappedStatementMetaInfo.getSqlCommandType();
        switch (sqlCommandType) {
            case INSERT:
                InsertBuilder.buildAndSet(mappedStatementMetaInfo);
                break;
            case SELECT:
                SelectBuilder.buildAndSet(mappedStatementMetaInfo);
                break;
            case UPDATE:
                UpdateBuilder.buildAndSet(mappedStatementMetaInfo);
                break;
            case DELETE:
                DeleteBuilder.buildAndSet(mappedStatementMetaInfo);
                break;
        }
    }


    /**
     * 获取缓存key
     *
     * @param id
     * @param runtimeParam
     * @return
     */
    private static String getCacheKeyByMappedStatementIdAndRuntimeParam(String id, Object runtimeParam) {
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        if (methodName.startsWith("selectVOList")) {
            Object runParamByIndex = MybatisUtil.getParamByClass(runtimeParam, Class.class);
            if (runParamByIndex instanceof Class) {
                Class<?> voClass = (Class<?>) runParamByIndex;
                return id + "_vo_" + voClass.getCanonicalName() + "_List";
            } else {
                throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
            }
        } else if (methodName.startsWith("selectVO")) {
            Object runParamByIndex = MybatisUtil.getParamByClass(runtimeParam, Class.class);
            if (runParamByIndex instanceof Class) {
                Class<?> voClass = (Class<?>) runParamByIndex;
                return id + "_vo_" + voClass.getCanonicalName();
            } else {
                throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
            }
        } else if (methodName.endsWith("DTO")) {
            Class dtoClass = runtimeParam.getClass();
            return id + "_dto_" + dtoClass.getCanonicalName();
        } else if (methodName.endsWith("DTOs")) {
            List list = MybatisUtil.getAndValidParamByClass(runtimeParam, List.class);
            Object dto = list.get(0);
            Class dtoClass = dto.getClass();
            return id + "_dto_" + dtoClass.getCanonicalName() + "_List";
        } else if (methodName.equals("updateDTOByCondition")) {
            Object dto = MybatisUtil.getAndValidParamByName(runtimeParam, BaseDao.DTO_PARAM);
            Object condition = MybatisUtil.getAndValidParamByName(runtimeParam, BaseDao.CONDITION_PARAM);
            return id + "_dto_" + dto.getClass().getCanonicalName() + "_condition_" + condition.getClass().getCanonicalName();
        } else if (methodName.startsWith("updateDTO")) {
            Class dtoClass = runtimeParam.getClass();
            return id + "_dto_" + dtoClass.getCanonicalName();
        } else if (methodName.endsWith("Condition")) {
            Object condition = MybatisUtil.getAndValidParamByName(runtimeParam, BaseDao.CONDITION_PARAM);
            return id + "_condition_" + condition.getClass().getCanonicalName();
        }
        return id;
    }

    /**
     * 从MappedStatementId中获取元信息
     *
     * @param mappedStatementId
     * @return 0:className, 1:methodName
     */
    private static String[] getMappedStatementIdMetaInfo(String mappedStatementId) {
        int lastPointPosition = mappedStatementId.lastIndexOf(".");
        String[] mappedStatementIdMetaInfo = new String[]{mappedStatementId.substring(0, lastPointPosition), mappedStatementId.substring(lastPointPosition + 1)};
        return mappedStatementIdMetaInfo;
    }

    /**
     * MappedStatementBaseInfo解析器
     *
     * @see MappedStatementBaseInfo
     * provider 入口: daoClass, method, runParam
     * interceptor 入口:  mappedStatement, runParam
     */
    static class MappedStatementBaseInfoParser {

        /**
         * 在Provider解析基础数据
         *
         * @param providerContext
         * @param runParam
         * @return
         */
        static MappedStatementBaseInfo parseInProvider(ProviderContext providerContext, Object runParam) {
            MappedStatementBaseInfo mappedStatementBaseInfo = new MappedStatementBaseInfo();
            Class<?> daoClass = providerContext.getMapperType();
            Method method = providerContext.getMapperMethod();
            String methodName = method.getName();
            String mappedStatementId = daoClass.getCanonicalName() + "." + methodName;
            SqlCommandType sqlCommandType = null;
            if (methodName.startsWith("select") || methodName.startsWith("get") || methodName.startsWith("count")) {
                sqlCommandType = SqlCommandType.SELECT;
            } else if (methodName.startsWith("insert")) {
                sqlCommandType = SqlCommandType.INSERT;
            } else if (methodName.startsWith("update")) {
                sqlCommandType = SqlCommandType.UPDATE;
            } else if (methodName.startsWith("delete")) {
                sqlCommandType = SqlCommandType.DELETE;
            }
            Class entityClass = getEntityClassByDaoClass(daoClass);

            mappedStatementBaseInfo.setMappedStatementId(mappedStatementId);
            mappedStatementBaseInfo.setDaoClassName(daoClass.getName());
            mappedStatementBaseInfo.setMethodName(methodName);
            mappedStatementBaseInfo.setSqlCommandType(sqlCommandType);
            mappedStatementBaseInfo.setDaoClass(daoClass);
            mappedStatementBaseInfo.setMethod(method);
            mappedStatementBaseInfo.setEntityClass(entityClass);

            dealParameterAndReturnType(runParam, mappedStatementBaseInfo, method);
            return mappedStatementBaseInfo;
        }

        /**
         * 在Interceptor解析基础数据
         *
         * @param mappedStatement
         * @param runParam
         * @return
         */
        static MappedStatementBaseInfo parseInInterceptor(MappedStatement mappedStatement, Object runParam) {
            MappedStatementBaseInfo mappedStatementBaseInfo = new MappedStatementBaseInfo();
            mappedStatementBaseInfo.setMappedStatement(mappedStatement);
            String mappedStatementId = mappedStatement.getId();
            int lastPointPosition = mappedStatementId.lastIndexOf(".");
            String daoClassName = mappedStatementId.substring(0, lastPointPosition);
            String methodName = mappedStatementId.substring(lastPointPosition + 1);
            Class daoClass = null;
            try {
                daoClass = Class.forName(daoClassName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Method method = null;
            if (null != daoClass) {
                method = ReflectUtil.getMethodByName(daoClass, methodName);
            }
            Class entityClass = getEntityClassByDaoClass(daoClass);

            mappedStatementBaseInfo.setMappedStatementId(mappedStatementId);
            mappedStatementBaseInfo.setDaoClassName(daoClassName);
            mappedStatementBaseInfo.setMethodName(methodName);
            mappedStatementBaseInfo.setSqlCommandType(mappedStatement.getSqlCommandType());
            mappedStatementBaseInfo.setDaoClass(daoClass);
            mappedStatementBaseInfo.setMethod(method);
            mappedStatementBaseInfo.setEntityClass(entityClass);

            dealParameterAndReturnType(runParam, mappedStatementBaseInfo, method);
            return mappedStatementBaseInfo;
        }


        /**
         * 处理MappedStatementReturnType
         *
         * @param runParam
         * @param mappedStatementBaseInfo
         * @param method
         */
        private static void dealMappedStatementReturnType(Object runParam, MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            //select语句时:
            /**
             * 1. 普通 返回值是普通非集合对象, Entity selectOne(id); 需要完全支持
             * 2. 视图 Vo selectOne(id); 需啊支持
             * 3. map Map<String, Object> selectOne(id); 需要支持
             * 4. 普通集合 List<Entity> selectList(condition); 需要完全支持
             * 5. 视图集合 List<Vo> selectList(condition); 需要支持
             * 6. ListMap List<Map<String,Object>> selectList(condition) 需要支持
             * 7. count int count*(condition) 需要支持
             */
            Class<?> returnType = method.getReturnType();
            mappedStatementBaseInfo.setReturnType(returnType);
            if (returnType == mappedStatementBaseInfo.getEntityClass()) {
                mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectEntity);
            } else if (returnType == Object.class) {
                Type type = method.getGenericReturnType();
                if (type instanceof TypeVariableImpl) {
                    if (((TypeVariableImpl<?>) type).getName().equals("Entity")) {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectEntity);
                    } else if (((TypeVariableImpl<?>) type).getName().equals("VO")) {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectVO);
                        Object runParamByIndex = MybatisUtil.getAndValidParamByClass(runParam, Class.class);
                        if (runParamByIndex instanceof Class) {
                            mappedStatementBaseInfo.setVoClass(((Class<?>) runParamByIndex));
                        } else {
                            throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                        }
                    } else {
                        throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                    }
                }
            } else if (returnType.isAssignableFrom(Map.class)) {
                mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectMap);
            } else if (returnType.isAssignableFrom(List.class)) {
                Type type = ((ParameterizedTypeImpl) method.getGenericReturnType()).getActualTypeArguments()[0];
                if (type instanceof TypeVariableImpl) {
                    if (((TypeVariableImpl<?>) type).getName().equals("Entity")) {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectEntity);
                    } else if (((TypeVariableImpl<?>) type).getName().equals("VO")) {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectVOs);
                        Object runParamByIndex = MybatisUtil.getAndValidParamByClass(runParam, Class.class);
                        if (runParamByIndex instanceof Class) {
                            mappedStatementBaseInfo.setVoClass(((Class<?>) runParamByIndex));
                        } else {
                            throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                        }
                    } else {
                        throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                    }
                } else if (type instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) type;
                    Class rawType = (Class) parameterizedType.getRawType();
                    if (rawType == Map.class) {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectMaps);
                    } else {
                        mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.selectVOs);
                        Class voClass = MybatisUtil.getAndValidParamByClass(runParam, Class.class);
                        mappedStatementBaseInfo.setVoClass(voClass);
                    }
                } else {
                    //出现了程序预料外的情况, List的泛型取法不是直接走TypeVariableImpl,请核实和补充代码
                    throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                }
            } else if (method.getName().startsWith("count") && returnType == int.class) {
                mappedStatementBaseInfo.setMappedStatementResultType(MappedStatementResultType.count);
            } else {
                throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
            }
        }

        /**
         * 处理MappedStatementParameterType
         *
         * @param runParam
         * @param mappedStatementBaseInfo
         * @param method
         */
        private static void dealMappedStatementParameterTypeWhenSelect(Object runParam, MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            String methodName = method.getName();
            Class<?>[] parameterTypes = ReflectUtil.getRealMethodParameterClasses(method, mappedStatementBaseInfo);
            if (parameterTypes.length > 0) {
                if (parameterTypes.length > 2) {
                    throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                } else {
                    if (methodName.endsWith("ByEntity")) {
                        mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.selectByEntity);
                    } else if (methodName.endsWith("ById") || methodName.endsWith("ByPrimaryKey")) {
                        mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.selectById);
                    } else {
                        mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.selectByCondition);
                        Object param = MybatisUtil.getAndValidOnlyParam(runParam);
                        mappedStatementBaseInfo.setConditionClass(param.getClass());
                    }

                    if (parameterTypes.length == 2) {
                        if (methodName.startsWith("selectVO")) {
                        } else {
                            throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                        }
                    }
                }
            } else {
                mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.selectEmpty);
            }
        }

        /**
         * 根据daoClass获取EntityClass
         *
         * @param daoClass
         * @return
         */
        private static Class getEntityClassByDaoClass(Class daoClass) {
            for (Type genericInterface : daoClass.getGenericInterfaces()) {
                ParameterizedType type = (ParameterizedType) genericInterface;
                if (type.getRawType() == BaseDao.class) {
                    Type[] actualTypeArguments = type.getActualTypeArguments();
                    return (Class) actualTypeArguments[0];
                } else {
                    ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                }
            }
            return null;
        }

        /**
         * 处理MappedStatementParameterType
         *
         * @param runParam
         * @param mappedStatementBaseInfo
         * @param method
         */
        private static void dealParameterAndReturnTypeWhenInsert(Object runParam,
                                                                 MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            String methodName = method.getName();
            Class<?>[] parameterTypes = ReflectUtil.getRealMethodParameterClasses(method, mappedStatementBaseInfo);
            Class entityClass = mappedStatementBaseInfo.getEntityClass();
            if (parameterTypes.length > 0) {
                if (parameterTypes.length > 1) {
                    throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                }
                Object param = MybatisUtil.getAndValidOnlyParam(runParam);
                if (List.class.isAssignableFrom(param.getClass())) {
                    Type type = method.getGenericParameterTypes()[0];
                    if (type instanceof ParameterizedType) {
                        ParameterizedType typeVariable = (ParameterizedType) type;
                        if (typeVariable.getActualTypeArguments()[0].getTypeName().equals("Entity")) {
                            mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.insertList);
                        } else if (methodName.endsWith("DTOs")) {
                            mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.insertDtoList);
                            List list = MybatisUtil.getAndValidParamByClass(runParam, List.class);
                            Object dto = list.get(0);
                            mappedStatementBaseInfo.setDtoClass(dto.getClass());
                        } else {
                            throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                        }
                    } else {
                        throw ExceptionFactory.jsonException(DaoExceptionEnum.CODE_DESIGN_ESTIMATE_NOT_ENOUGH);
                    }
                } else if (methodName.endsWith("DTO")) {
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.insertDto);
                    Object onlyParam = MybatisUtil.getAndValidOnlyParam(runParam);
                    mappedStatementBaseInfo.setDtoClass(onlyParam.getClass());
                } else if (runParam.getClass() == entityClass) {
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.insertEntity);
                }
            }

            Class<?> returnType = method.getReturnType();
            mappedStatementBaseInfo.setReturnType(returnType);
        }

        /**
         * 处理MappedStatementParameterType
         *
         * @param runParam
         * @param mappedStatementBaseInfo
         * @param method
         */
        private static void dealParameterAndReturnTypeWhenUpdate(Object runParam, MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            String methodName = method.getName();
            switch (methodName) {
                case "updateEntity":
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateEntity);
                    break;
                case "updateDTO": {
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateDto);
                    Object dto = MybatisUtil.getAndValidOnlyParam(runParam);
                    mappedStatementBaseInfo.setDtoClass(dto.getClass());
                    break;
                }
                case "updateEntityIgnoreNull":
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateEntityIgnoreNull);
                    break;
                case "updateDTOIgnoreNull": {
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateDtoIgnoreNull);
                    Object dto = MybatisUtil.getAndValidOnlyParam(runParam);
                    mappedStatementBaseInfo.setDtoClass(dto.getClass());
                    break;
                }
                case "updateEntityByCondition": {
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateEntityByCondition);
                    Object condition = MybatisUtil.getAndValidParamByName(runParam, BaseDao.CONDITION_PARAM);
                    mappedStatementBaseInfo.setConditionClass(condition.getClass());
                    break;
                }
                case "updateDTOByCondition":
                    mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.updateDtoByCondition);
                    Object condition = MybatisUtil.getAndValidParamByName(runParam, BaseDao.CONDITION_PARAM);
                    Object dto = MybatisUtil.getAndValidParamByName(runParam, BaseDao.DTO_PARAM);
                    mappedStatementBaseInfo.setConditionClass(condition.getClass());
                    mappedStatementBaseInfo.setDtoClass(dto.getClass());
                    break;
            }
            Class<?> returnType = method.getReturnType();
            mappedStatementBaseInfo.setReturnType(returnType);
        }

        private static void dealParameterAndReturnTypeWhenDelete(Object runParam, MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            String methodName = method.getName();
            if (methodName.equals("deleteById")) {
                mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.deleteById);
            } else if (methodName.equals("delete")) {
                mappedStatementBaseInfo.setMappedStatementParameterType(MappedStatementParameterType.deleteByEntity);
            }
            mappedStatementBaseInfo.setReturnType(method.getReturnType());
        }

        private static void dealParameterAndReturnType(Object runParam, MappedStatementBaseInfo mappedStatementBaseInfo, Method method) {
            SqlCommandType sqlCommandType = mappedStatementBaseInfo.getSqlCommandType();
            if (sqlCommandType == SqlCommandType.SELECT) {
                dealMappedStatementParameterTypeWhenSelect(runParam, mappedStatementBaseInfo, method);
                dealMappedStatementReturnType(runParam, mappedStatementBaseInfo, method);
            } else if (sqlCommandType == SqlCommandType.INSERT) {
                dealParameterAndReturnTypeWhenInsert(runParam, mappedStatementBaseInfo, method);
            } else if (sqlCommandType == SqlCommandType.UPDATE) {
                dealParameterAndReturnTypeWhenUpdate(runParam, mappedStatementBaseInfo, method);
            } else if (sqlCommandType == SqlCommandType.DELETE) {
                dealParameterAndReturnTypeWhenDelete(runParam, mappedStatementBaseInfo, method);
            }
        }
    }

}
